"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const colyseus_1 = require("colyseus");
const core_1 = require("@colyseus/core");
const lodash_1 = require("lodash");
const GameSchema_1 = require("../schema/GameSchema");
const constant_1 = require("../common/constant");
const enum_1 = require("../typing/enum");
const helper_1 = require("../common/helper");
const shuffle_1 = __importDefault(require("../common/shuffle"));
const config_1 = __importDefault(require("../config"));
const robot_1 = __importDefault(require("../common/robot"));
// 1到54是按照扑克牌的顺序依次编号的，其中1到13代表方块（Diamonds），14到26代表梅花（Clubs），27到39代表红桃（Hearts），40到52代表黑桃（Spades），53和54为大小王
class GameRoom extends colyseus_1.Room {
    constructor() {
        super(...arguments);
        this.maxClients = 3;
        // 是否自动关闭
        this.autoDispose = false;
        /** 当前轮次 */
        this.currentRoundId = -1;
        /** 初始化座位状态 */
        this.seats = new Array(this.maxClients).fill(null);
        // 操作记录
        this.records = [];
        this.pastRecords = [];
    }
    /** 是否已经开始游戏 */
    get isGaming() {
        return this.state.roundState !== enum_1.ROUND_STATE.IDLE;
    }
    /** 是否满员 */
    get isFull() {
        return this.state.players.size >= this.maxClients;
    }
    /** 上一手出的牌 */
    get lastPlayedCards() {
        var _a;
        return (((_a = (0, lodash_1.findLast)(this.records, (item) => item.type === enum_1.Move.Play)) === null || _a === void 0 ? void 0 : _a.playCards) || []);
    }
    /** 已出的牌 */
    get playedCards() {
        return this.records.filter(item => item.type === enum_1.Move.Play).reduce((prev, cur) => {
            return [...prev, ...cur.playCards];
        }, []);
    }
    ;
    /**
     * 给用户分配座位
     * @param userId
     * @returns
     */
    allocateSeat(userId) {
        for (let i = 0; i < this.maxClients; i++) {
            if (this.seats[i] === null) {
                this.seats[i] = userId;
                return i; // 返回座位号
            }
        }
        return -1; // 没有空座位
    }
    /**
     * 释放座位
     * @param userId
     */
    releaseSeat(userId) {
        for (let i = 0; i < this.maxClients; i++) {
            if (this.seats[i] === userId) {
                this.seats[i] = null;
                break;
            }
        }
    }
    /**
     * 添加用户操作记录
     * @param sessionId
     * @param type
     * @param playCards
     */
    addRecord(sessionId, type, playCards) {
        this.records.push({
            sessionId,
            type,
            playCards,
        });
        if (type === enum_1.Move.Play) {
            this.state.playedCardsNum += playCards.length;
        }
    }
    /**
     * 清除记录
     */
    clearRecord() {
        this.pastRecords = [...this.pastRecords, ...this.records];
        this.records = [];
        this.state.playedCardsNum = 0;
    }
    delay(ms) {
        return new Promise((resolve) => this.clock.setTimeout(resolve, ms));
    }
    /**
     * 开始发牌
     */
    startDeal() {
        // 每个玩家卡牌数量
        const cardsCount = Math.floor(constant_1.POKER_TOTAL_NUM / this.maxClients);
        const toDealList = Array.from({ length: 3 }, () => []);
        this.dealer.deal(cardsCount, toDealList);
        this.state.players.forEach((player) => {
            const leftCards = toDealList.pop();
            if (leftCards) {
                player.leftCards = leftCards;
                player.leftCount = player.leftCards.length;
            }
        });
    }
    // 检测是否可开始
    startCheck() {
        return __awaiter(this, void 0, void 0, function* () {
            // 人数未满
            if (!this.isFull)
                return;
            // 非空闲态
            if (this.state.roundState !== enum_1.ROUND_STATE.IDLE)
                return;
            // 发牌
            this.startDeal();
            // 发牌中
            this.state.roundState = enum_1.ROUND_STATE.DEALING;
            // 等待发牌
            yield this.delay(config_1.default.DEALING_TIME);
            this.state.roundState = enum_1.ROUND_STATE.TURNS;
            // 随机选择开始
            this.startRound((0, lodash_1.random)(0, this.maxClients - 1));
        });
    }
    /**
     * 开始下一轮次 传入指定id代表需要更新guessId
     * @param roundId
     */
    startRound(roundId) {
        this.clearDelayedRoundStartRef();
        const isNewly = roundId !== undefined;
        this.currentRoundId = isNewly ? roundId : (this.currentRoundId + 1) % this.maxClients;
        core_1.logger.info("当前轮次", this.roomId, this.currentRoundId);
        // 需要选牌则加5秒时间
        const delay = config_1.default.PLAY_TIME + (isNewly ? config_1.default.SELECT_ADD_TIME : 0);
        const now = Date.now();
        this.state.startTime = now + delay;
        this.state.currentTime = now;
        this.state.players.forEach((player) => {
            if (player.roundId === this.currentRoundId) {
                player.status = isNewly ? enum_1.USER_STATUS.WAITING_SELECT : enum_1.USER_STATUS.WAITING_PLAY;
                // 玩家离线，则由机器人出
                if (!player.connected) {
                    this.allowRobotPlay(player);
                }
            }
            else {
                player.status = enum_1.USER_STATUS.FINISHED;
            }
        });
        // 轮次切换,会多出两秒的超时
        this.delayedRoundStartRef = this.clock.setTimeout(() => __awaiter(this, void 0, void 0, function* () {
            var _a;
            let currentPlayer;
            this.state.players.forEach((player) => {
                if (player.roundId === this.currentRoundId) {
                    currentPlayer = player;
                }
            });
            if (!(currentPlayer)) {
                this.startRound();
                return;
            }
            // 玩家未出牌
            if (((_a = this.records[this.records.length - 1]) === null || _a === void 0 ? void 0 : _a.sessionId) !== currentPlayer.sessionId) {
                this.allowRobotPlay(currentPlayer, 0);
            }
        }), delay + config_1.default.TURN_OUT_TIME);
    }
    allowRobotPlay(player, delay) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!player.robot) {
                player.robot = new robot_1.default(player.sessionId, this);
            }
            // 机器人出牌
            yield player.robot.play(delay);
        });
    }
    /**
     * 判断是否为当前用户轮
     * @param sessionId
     * @returns
     */
    checkIsInRound(sessionId) {
        return this.state.players.get(sessionId).roundId === this.currentRoundId;
    }
    /**
     * 判断用户在当前游戏中
     * @param sessionId
     * @returns
     */
    checkIsInCurrentGame(sessionId) {
        return this.state.players.has(sessionId);
    }
    /**
     * 简单判断牌的有效性
     * @param cards
     * @returns
     */
    checkCardsIsValid(cards) {
        return Array.isArray(cards) && cards.length >= 1 && cards.length <= 4;
    }
    /**
     * 获取用户
     * @param sessionId
     * @returns
     */
    getPlayerById(sessionId) {
        return this.state.players.get(sessionId);
    }
    /**
     * 给用户添加牌
     * @param id
     * @param cards
     */
    addCardsToPlayer(id, cards) {
        const player = this.getPlayerById(id);
        player.leftCards = [...player.leftCards, ...cards];
        player.leftCount = player.leftCards.length;
    }
    /**
     * 出牌
     * @param params
     * @returns
     */
    onPlay(params) {
        return __awaiter(this, void 0, void 0, function* () {
            const { sessionId, playCards, guessCardId } = params;
            core_1.logger.info("出牌", this.roomId, params);
            // 不在该游戏内
            if (!this.checkIsInCurrentGame(sessionId)) {
                core_1.logger.warn("非当前游戏用户");
                return {
                    success: "false",
                    ret: "NOT_IN_GAME",
                };
            }
            // 牌无效
            if (!this.checkCardsIsValid(playCards)) {
                core_1.logger.warn("牌无效");
                return {
                    success: "false",
                    ret: "CARDS_IS_INVALID",
                };
            }
            // 不该自己出牌
            if (!this.checkIsInRound(sessionId)) {
                core_1.logger.warn("非当前用户轮次");
                return {
                    success: "false",
                    ret: "NOT_OWN_TURN",
                };
            }
            this.clearDelayedRoundStartRef();
            const player = this.getPlayerById(sessionId);
            if (guessCardId && player.status === enum_1.USER_STATUS.WAITING_SELECT) {
                this.state.currentGuessCard = (0, helper_1.getCardValue)(Number(guessCardId));
                core_1.logger.info("用户选的牌", this.roomId, this.state.currentGuessCard);
            }
            // 出牌
            player.leftCards = (0, lodash_1.difference)(player.leftCards, playCards);
            player.leftCount = player.leftCards.length;
            // 用户出的牌的数量
            player.playCardsCount = playCards.length;
            // 修改用户状态
            player.opStatus = enum_1.OP_STATUS.PLAY;
            // 添加记录
            this.addRecord(sessionId, enum_1.Move.Play, playCards);
            // 牌出完，结束
            if (player.leftCount === 0) {
                this.state.players.forEach((item) => {
                    item.isWin = item.sessionId === sessionId;
                });
                this.state.roundState = enum_1.ROUND_STATE.END;
                core_1.logger.info("结束", this.roomId, player.sessionId);
            }
            else {
                this.startRound();
            }
            return {
                success: "true",
            };
        });
    }
    /**
     * 过牌,两次过则移除出的牌
     * @param params
     * @returns
     */
    onPass(params) {
        return __awaiter(this, void 0, void 0, function* () {
            const { sessionId } = params;
            core_1.logger.info('玩家过', this.roomId, sessionId);
            // 不该自己出牌
            if (!this.checkIsInRound(sessionId)) {
                return {
                    success: "false",
                    ret: "NOT_OWN_TURN",
                };
            }
            this.clearDelayedRoundStartRef();
            this.addRecord(sessionId, enum_1.Move.Pass, []);
            // 是否全都是过
            const isAllPass = (0, lodash_1.every)((0, lodash_1.takeRight)(this.records, this.state.players.size - 1), (item) => item.type === enum_1.Move.Pass);
            core_1.logger.info('全都过', this.roomId, isAllPass);
            if (isAllPass) {
                // 出牌的人先出
                const lastPlayRecord = (0, lodash_1.findLast)(this.records, (item) => item.type === enum_1.Move.Play);
                core_1.logger.info('出牌的人先出', this.roomId, lastPlayRecord, JSON.stringify(this.records));
                const nextRoundId = this.getPlayerById(lastPlayRecord === null || lastPlayRecord === void 0 ? void 0 : lastPlayRecord.sessionId).roundId;
                this.clearRecord();
                core_1.logger.info('过牌-下一个', this.roomId, nextRoundId);
                this.startRound(nextRoundId);
            }
            else {
                this.startRound();
            }
            return {
                success: "true",
            };
        });
    }
    /**
     * 掀牌
     * @param params
     * @returns
     */
    onLift(params) {
        const { sessionId } = params;
        core_1.logger.info('玩家掀牌', this.roomId, sessionId);
        // 不该自己出牌
        if (!this.checkIsInRound(sessionId)) {
            return {
                success: "false",
                ret: "NOT_OWN_TURN",
            };
        }
        this.clearDelayedRoundStartRef();
        // 最后出牌的人
        const lastPlayedRecord = (0, lodash_1.findLast)(this.records, (item) => item.type === enum_1.Move.Play);
        const targetId = lastPlayedRecord === null || lastPlayedRecord === void 0 ? void 0 : lastPlayedRecord.sessionId;
        const lastPlayedCards = (lastPlayedRecord === null || lastPlayedRecord === void 0 ? void 0 : lastPlayedRecord.playCards) || [];
        core_1.logger.info('最后出的牌', this.roomId, lastPlayedCards);
        this.addRecord(sessionId, enum_1.Move.Lift, []);
        // 出的牌是否和猜牌一致，（不一致则代表猜对）
        const isCoincident = lastPlayedCards.every((item) => (0, helper_1.isSameCardValue)(item, this.state.currentGuessCard) || (0, helper_1.isJoker)(item));
        core_1.logger.info('是否猜对', this.roomId, !isCoincident);
        const executeId = isCoincident ? sessionId : targetId;
        this.addCardsToPlayer(executeId, this.playedCards);
        // 掀错或者被掀对的人先出
        const nextRoundId = this.getPlayerById(executeId).roundId;
        const isCorrect = !isCoincident;
        this.state.liftDetails.isCorrect = isCorrect;
        this.state.liftDetails.fromId = sessionId;
        this.state.liftDetails.toId = targetId;
        this.state.liftDetails.lastPlayedCards = lastPlayedCards;
        this.state.liftDetails.lastPlayedCardsCount = lastPlayedCards.length;
        this.state.liftDetails.deskCards = this.playedCards;
        this.state.liftDetails.deskCardsCount = this.playedCards.length;
        this.getPlayerById(sessionId).opStatus = enum_1.OP_STATUS.LIFT;
        this.delay(config_1.default.LIFT_NEXT_TIME).then(() => {
            this.clearRecord();
            this.startRound(nextRoundId);
        });
        return {
            success: "true",
            data: {
                isCorrect,
                lastCards: lastPlayedCards,
                deskCards: isCorrect ? [] : this.playedCards,
                deskCardsCount: isCorrect ? this.playedCards.length : 0,
                targetId: isCorrect ? targetId : sessionId,
            },
        };
    }
    // 房间初始化时
    onCreate() {
        // 初始化房间状态
        this.setState(new GameSchema_1.RoomState());
        this.dealer = new shuffle_1.default();
    }
    // 入场检验
    onAuth() {
        if (this.isFull) {
            throw new colyseus_1.ServerError(5555, "ROOM_IS_FULL");
        }
        return true;
    }
    waitRobotJoin() {
        if (this.isFull)
            return;
        this.delayedRobotJoinRef = this.clock.setTimeout(() => {
            if (this.isFull)
                return;
            const sessionId = `robot_${this.state.players.size + 1}`;
            this.onJoin({ sessionId }, {});
        }, config_1.default.ROBOT_JOIN_TIME);
    }
    clearDelayedRobotJoinRef() {
        if (this.delayedRobotJoinRef) {
            this.delayedRobotJoinRef.clear();
            this.delayedRobotJoinRef = undefined;
        }
    }
    clearDelayedRoundStartRef() {
        if (this.delayedRoundStartRef) {
            this.delayedRoundStartRef.clear();
            this.delayedRoundStartRef = undefined;
        }
    }
    // 当客户端成功加入房间时
    onJoin(client, options) {
        this.clearDelayedRobotJoinRef();
        const { sessionId } = client;
        core_1.logger.info('用户加入', this.roomId, sessionId);
        const player = new GameSchema_1.Player(sessionId, this.allocateSeat(sessionId));
        this.state.players.set(sessionId, player);
        this.startCheck();
        this.waitRobotJoin();
    }
    // 当客户端离开房间时，consented断开连接是由客户端发起的
    onLeave(client, consented) {
        return __awaiter(this, void 0, void 0, function* () {
            const { sessionId } = client;
            if (!this.isGaming) {
                // 移除客户端
                if (this.state.players.has(sessionId)) {
                    this.releaseSeat(sessionId);
                    this.state.players.delete(sessionId);
                }
            }
            else {
                if (this.state.players.has(sessionId)) {
                    this.getPlayerById(sessionId).connected = false;
                }
            }
            yield this.allowReconnection(client, "manual");
            this.getPlayerById(sessionId).connected = true;
        });
    }
    // 析构函数, 当房间里没有客户端时被调用. (参考 `autoDispose`)
    onDispose() {
        // 断开所有客户断, 然后销毁房间.
        // this.disconnect();
    }
}
exports.default = GameRoom;
